local assets_doro_fly_sword = {
    Asset("ANIM", "anim/doro_fly_sword.zip")

}
local function CalculateAimPos(a, b, c, d)
    if not a.x then
        a = a:GetPosition()
    end
    if not b.x then
        b = b:GetPosition()
    end
    if b.x == a.x then
        b.x = b.x + 0.1
    end
    if b.z == a.z then
        b.z = b.z + 0.1
    end
    local dx, dz = b.x - a.x, b.z - a.z
    local distance = math.sqrt(dx * dx + dz * dz)
    local cos, sin = dx / distance, dz / distance
    local aimdx = d * (math.cos(c) * cos - math.sin(c) * sin)
    local aimdz = d * (math.sin(c) * cos + math.cos(c) * sin)
    local aimx, aimz = a.x + aimdx, a.z + aimdz
    return Vector3(aimx, 0, aimz)
end
local function RemoveSwordFromList(sword, player)
    player.doro_swordtable = player.doro_swordtable or {}
    if player.doro_swordtable[sword] then
        player.sword_nums = (player.sword_nums or 0) - 1
        if player.sword_nums < 0 then
            player.sword_nums = 0
        end
        player.doro_swordtable[sword] = nil
        sword.lastowner = player
    end
end
local function AddSwordToList(sword, player)
    player.sword_nums = (player.sword_nums or 0) + 1

    player.doro_swordtable = player.doro_swordtable or {}
    player.doro_swordtable[sword] = sword
end
local function shouldKeetTarget(inst, target)
    local owner = inst.lastowner or inst.owner
    local disq = distsq(target:GetPosition(), owner:GetPosition())
    return target:IsValid() and not target:HasTag("isdead") and disq < (20 * 20)
end
local s = 3
local dir = math.max(math.ceil(360 / (30 * s)), 1)
local function onattackother(inst, data, player)
    local v = data and data.target
    if v then
        local dsq = v:GetDistanceSqToInst(player)
        local health = v.components.health
        if inst.target ~= v and dsq < (20 * 20) and health and not health:IsDead()
            and shouldKeetTarget(inst, v) then
            inst.target = v
            RemoveSwordFromList(inst, player)
            inst:DoSwordAttack(v, 1)
        end
    end
end
local function onremovelist(inst)
    if inst.owner then
        RemoveSwordFromList(inst, inst.owner)
    end
end
local function SetOwner(inst, player, params)
    inst.AnimState:SetOrientation(ANIM_ORIENTATION.Default)
    inst.Physics:SetMotorVel(0, 0, 0)
    inst.Physics:SetActive(false)
    inst.Transform:SetRotation(0)

    inst.target = nil
    inst.owner = player
    AddSwordToList(inst, player)

    inst:RemoveEventCallback("onremove", onremovelist)
    inst:ListenForEvent("onremove", onremovelist)

    if not player.doro_followowner_task then
        local organdseita = 0
        player.doro_followowner_task = player:DoPeriodicTask(1 * FRAMES, function()
            organdseita = organdseita + 1.5
            if organdseita >= 360 then
                organdseita = organdseita - 360
            end
            local ppos = player:GetPosition()
            local zeropos = player:GetPosition() + Vector3(1, 0, 0)
            local index = 0
            local dseita = 360 / (player.sword_nums or 1) * DEGREES
            for k, v in pairs(player.doro_swordtable) do
                index = index + 1
                local aimpos = CalculateAimPos(ppos, zeropos, dir * DEGREES * organdseita + index * dseita, 4)
                --[[   k.Physics:Teleport(aimpos:Get()) ]]
                k:SmoothSetPosition(aimpos)
            end
        end)
    end

    if not inst._dorosword_onattackother then
        inst._dorosword_onattackother = function(_, data)
            onattackother(inst, data, player)
        end
        inst:ListenForEvent("onattackother", inst._dorosword_onattackother, player)
    end

    if inst.trytargeting_task then
        inst.trytargeting_task:Cancel()
    end
    local function targetTask()
        if inst.owner:IsValid() then
            local x, y, z = inst.owner.Transform:GetWorldPosition()
            local ents = TheSim:FindEntities(x, y, z, 20, { "_combat" }, { "INLIMBO" })
            local minhealth, mintarget
            for k, v in pairs(ents) do
                local target = v.components.combat and v.components.combat.target
                local health = v.components.health
                local currenthealth = health and health.currenthealth
                if target and target:HasTag("player")
                    and health and currenthealth and not health:IsDead()
                    and not v:HasTag("structure")
                    and shouldKeetTarget(inst, v) then
                    if not minhealth or currenthealth < minhealth then
                        mintarget = v
                        minhealth = currenthealth
                    end
                end
            end
            if mintarget then
                local v = mintarget

                inst.target = v
                RemoveSwordFromList(inst, inst.owner)
                inst:DoSwordAttack(v, 2)
            end
        end
    end
    targetTask()
    inst.trytargeting_task = inst:DoPeriodicTask(0.5, targetTask)
end
local function CalcDamage(player, target, weapon, multiplier)
    if target:HasTag("alwaysblock") then
        return 0
    end
    local mult = 1
    if player.components.electricattacks ~= nil then
        if not
            (
                target:HasTag("electricdamageimmune") or
                (target.components.inventory ~= nil and target.components.inventory:IsInsulated())
            )
        then
            local electric_damage_mult = TUNING.ELECTRIC_DAMAGE_MULT
            local electric_wet_damage_mult = TUNING.ELECTRIC_WET_DAMAGE_MULT
            mult = electric_damage_mult +
                electric_wet_damage_mult *
                (target.components.moisture ~= nil and target.components.moisture:GetMoisturePercent() or (target:GetIsWet() and 1 or 0))
        end
    end



    local self = player.components.combat
    local basedamage
    local basemultiplier = self.damagemultiplier
    local externaldamagemultipliers = self.externaldamagemultipliers
    local damagetypemult = 1
    local bonus = self.damagebonus --not affected by multipliers
    local playermultiplier = target ~= nil and (target:HasTag("player") or target:HasTag("player_damagescale"))
    local pvpmultiplier = playermultiplier and self.inst:HasTag("player") and self.pvp_damagemod or 1
    local mount = nil

    basedamage = 33
    if self.inst.components.damagetypebonus ~= nil then
        damagetypemult = self.inst.components.damagetypebonus:GetBonus(target)
    end

    local damage = (basedamage or 0)
        * (basemultiplier or 1)
        * externaldamagemultipliers:Get()
        * damagetypemult
        * (multiplier or 1)
        * mult
        * pvpmultiplier
        * (self.customdamagemultfn ~= nil and self.customdamagemultfn(self.inst, target, weapon, multiplier, mount) or 1)
        + (bonus or 0)
    return damage
end
local function hitTarget(inst, target)
    local owner = inst.lastowner
    if owner then --飞剑 每次攻击都会扣1%理智
        if target.components.combat then
            local dmg = CalcDamage(owner, target, nil, 1)
            local spdamage = { planar = dmg }
            target.components.combat:GetAttacked(owner, 0, nil, nil, spdamage)
        end
        if owner.components.sanity then
            owner.components.sanity:DoDelta(-owner.components.sanity:GetMaxWithPenalty() * 0.01)
        end
    end
end
local function cancelSwordTask(inst)
    if inst.swordattack_task then
        inst.swordattack_task:Cancel()
        inst.swordattack_task = nil
    end
    if inst.swordattackpst_task then
        inst.swordattackpst_task:Cancel()
        inst.swordattackpst_task = nil
    end
    if inst.trytargeting_task then
        inst.trytargeting_task:Cancel()
        inst.trytargeting_task = nil
    end
end
local function OnRetire(inst, player)
    if player.doro_followowner_task then
        player.doro_followowner_task:Cancel()
        player.doro_followowner_task = nil
    end
    cancelSwordTask(inst)
end
local function smoothRot(inst, newrot, perrot)
    local nowrot = inst.Transform:GetRotation()
    if newrot < -180 then
        newrot = newrot + 360
    end
    perrot = perrot or 8
    if nowrot < newrot then --40 to 180
        local dt = newrot - nowrot
        if dt > 180 then
            inst.Transform:SetRotation(nowrot - math.min(360 - dt, perrot))
        else
            inst.Transform:SetRotation(nowrot + math.min(dt, perrot))
        end
    else --180 to 40
        local dt = nowrot - newrot
        if dt < 180 then
            inst.Transform:SetRotation(nowrot - math.min(dt, perrot))
        else
            inst.Transform:SetRotation(nowrot + math.min(360 - dt, perrot))
        end
    end
end
local WEIGHTED_TAIL_FXS =
{
    ["tail_5_8"] = 1,
    ["tail_5_9"] = .5,
}

local function CreateTail(bank, build, lightoverride, addcolour, multcolour)
    local inst = CreateEntity()

    inst:AddTag("FX")
    inst:AddTag("NOCLICK")
    --[[Non-networked entity]]
    inst.entity:SetCanSleep(false)
    inst.persists = false

    inst.entity:AddTransform()
    inst.entity:AddAnimState()

    MakeInventoryPhysics(inst)
    inst.Physics:ClearCollisionMask()

    inst.AnimState:SetBank(bank)
    inst.AnimState:SetBuild(build)
    inst.AnimState:PlayAnimation(weighted_random_choice(WEIGHTED_TAIL_FXS))
    inst.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround)
    if addcolour ~= nil then
        inst.AnimState:SetAddColour(unpack(addcolour))
        --[[ inst.AnimState:SetAddColour(math.random(), math.random(), math.random(), 0) ]]
    end
    if multcolour ~= nil then
        inst.AnimState:SetMultColour(unpack(multcolour))
    end
    if lightoverride > 0 then
        inst.AnimState:SetLightOverride(lightoverride)
    end
    inst.AnimState:SetFinalOffset(3)

    inst:ListenForEvent("animover", inst.Remove)

    --[[  inst:DoTaskInTime(0.2, inst.Remove) ]]
    return inst
end
local function OnUpdateProjectileTail(inst, bank, build, speed, lightoverride, addcolour, multcolour, hitfx, tails)
    local x, y, z = inst.Transform:GetWorldPosition()
    for tail, _ in pairs(tails) do
        tail:ForceFacePoint(x, y, z)
    end

    if inst.removing or inst:HasTag("removing") then
        for tail, _ in pairs(tails) do
            tail:Remove()
        end
    elseif inst.entity:IsVisible() and not inst.bantail then
        local tail = CreateTail(inst.buildandbank or bank, inst.buildandbank or build, lightoverride, addcolour,
            multcolour)
        local rot = inst.Transform:GetRotation()
        tail.Transform:SetRotation(rot + 90)
        rot = rot * DEGREES
        local offsangle = math.random() * TWOPI
        local offsradius = math.random() * .2 + .2
        local hoffset = math.cos(offsangle) * offsradius
        local voffset = math.sin(offsangle) * offsradius
        tail.Transform:SetPosition(x + math.sin(rot) * hoffset, y + voffset, z + math.cos(rot) * hoffset)
        tail.Physics:SetMotorVel(speed * (.2 + math.random() * .3), 0, 0)
        tails[tail] = true
        inst:ListenForEvent("onremove", function(tail) tails[tail] = nil end, tail)
        tail:ListenForEvent("onremove", function(inst)
            tail.Transform:SetRotation(tail.Transform:GetRotation() + math.random() * 30 - 15)
        end, inst)
    end
end

local function DoSwordAttack(inst, target, cause)
    inst.Physics:SetActive(true)
    inst.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround)
    cancelSwordTask(inst)
    local targetradius = target:GetPhysicsRadius(0) + 0.5 + 2
    inst.mode = "attack_pre"
    local minspeed = 12 * 1.5
    inst.fly_speed = minspeed
    local maxspeed = 30 * 1.5
    local tail = {}
    local red = { 1, 0, 0, 0 }
    inst.swordattack_task = inst:DoPeriodicTask(0, function()
        if not shouldKeetTarget(inst, target) then
            cancelSwordTask(inst)
            if inst.lastowner and inst.lastowner:IsValid() then
                inst:SetOwner(inst.lastowner)
            else
                inst:Remove()
            end
            return
        end
        OnUpdateProjectileTail(inst, "lavaarena_blowdart_attacks",
            "lavaarena_blowdart_attacks", inst.fly_speed, 0,
            red, nil, nil, tail)
        if inst.mode == "attack_pre" then
            inst.fly_speed = math.clamp(inst.fly_speed + 2, minspeed, maxspeed)
            local pos = inst:GetPosition()
            local tpos = target:GetPosition()
            local distance = math.sqrt((pos.x - tpos.x) * (pos.x - tpos.x) + (pos.z - tpos.z) * (pos.z - tpos.z))
            local newrot = inst:GetAngleToPoint(tpos:Get()) - 90
            smoothRot(inst, newrot, 360)
            inst.Physics:SetMotorVel(0, 0, -inst.fly_speed)
            if distance < targetradius then
                hitTarget(inst, target)
                inst.fly_speed = math.clamp(inst.fly_speed - 8, minspeed, maxspeed)
                inst.mode = "attack_hit"
            end
        elseif inst.mode == "attack_hit" then
            inst.Physics:SetMotorVel(0, 0, -inst.fly_speed)
            inst.mode = "attack_pst"
        elseif inst.mode == "attack_pst" then
            inst.fly_speed = math.clamp(inst.fly_speed + 0.7, minspeed, maxspeed)
            local pos = inst:GetPosition()
            local tpos = target:GetPosition()
            local distance = math.sqrt((pos.x - tpos.x) * (pos.x - tpos.x) + (pos.z - tpos.z) * (pos.z - tpos.z))
            if distance > 12 then
                inst.mode = "attack_turnrot"
            end
        elseif inst.mode == "attack_turnrot" then
            inst.Physics:SetMotorVel(0, 0, -10)
            -- -180 ~180
            local tpos = target:GetPosition()
            local nowrot = inst.Transform:GetRotation()
            local newrot = inst:GetAngleToPoint(tpos:Get()) - 90
            if newrot < -180 then
                newrot = newrot + 360
            end
            smoothRot(inst, newrot, 12)
            if math.abs(newrot - nowrot) < 5 or math.abs(newrot - nowrot) > 355 then
                inst.mode = "attack_pre"
            end
        end
    end)
end
local function SmoothSetPosition(inst, pos)
    if inst.max_transport_distsq and inst:IsValid() then
        local nowpos = inst:GetPosition()
        local aimpos = pos
        local disq = distsq(nowpos, aimpos)
        if disq > 80 * 80 then
            inst.Transform:SetPosition(aimpos.x, 0, aimpos.z)
        elseif disq > inst.max_transport_distsq then
            local finalpos = CalculateAimPos(nowpos, aimpos, 0, math.sqrt(inst.max_transport_distsq))
            inst.Transform:SetPosition(finalpos.x, 0, finalpos.z)
        else
            inst.Transform:SetPosition(aimpos.x, 0, aimpos.z)
        end
    end
end
local function doro_fly_sword()
    local inst = CreateEntity()
    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddSoundEmitter()
    --[[ inst.entity:AddLight() ]]
    inst.entity:AddNetwork()

    inst._ismastersim = TheWorld.ismastersim

    --[[  local rad = 4
    local intentsity = Lerp(0.8, 0.7, 0.5)
    local falloff = Lerp(0.8, 0.7, 0.5)
    inst.Light:SetFalloff(falloff)
    inst.Light:SetIntensity(intentsity)
    inst.Light:SetRadius(rad)

    inst.Light:SetColour(unpack({ 223 / 255, 208 / 255, 69 / 255 }))
    inst.Light:Enable(true)
    inst.Light:EnableClientModulation(true) ]]

    inst.AnimState:SetBank("doro_fly_sword")
    inst.AnimState:SetBuild("doro_fly_sword")
    inst.AnimState:PlayAnimation("idle")
    inst.AnimState:PushAnimation("idle", true)

    MakeProjectilePhysics(inst)

    --[[ inst.AnimState:SetBloomEffectHandle("shaders/anim.ksh") ]]

    inst:AddTag("ignorewalkableplatforms")

    inst.entity:SetPristine()

    if not inst._ismastersim then
        return inst
    end
    inst.persists = false

    inst.maxspeed = 6 * 6
    inst.max_transport_distsq = (inst.maxspeed / 30) ^ 2
    inst.DoSwordAttack = DoSwordAttack

    inst.SetOwner = SetOwner
    inst.SmoothSetPosition = SmoothSetPosition
    inst.RemoveSwordFromList = RemoveSwordFromList

    inst.OnRetire = OnRetire

    inst:AddComponent("inspectable")
    --[[
    inst:AddComponent("planardamage")
    inst.components.planardamage:SetBaseDamage(33) ]]
    return inst
end

return Prefab('doro_fly_sword', doro_fly_sword, assets_doro_fly_sword)
